package com.ruoyi.work.service.impl;

import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.util.ObjectUtil;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.SimpleBookmark;
import com.itextpdf.text.pdf.parser.ImageRenderInfo;
import com.itextpdf.text.pdf.parser.PdfReaderContentParser;
import com.itextpdf.text.pdf.parser.RenderListener;
import com.itextpdf.text.pdf.parser.TextRenderInfo;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.exception.DataException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.work.config.PdfSignatureConfig;
import com.ruoyi.work.domain.KeywordInfo;
import com.ruoyi.work.domain.Stamp;
import com.ruoyi.work.service.IPdfService;
import com.ruoyi.work.utils.PdfUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

/**
 * pdfService业务层实现
 *
 * @author fengcheng
 */
@Service
public class PdfServiceImpl implements IPdfService {

    @Autowired
    private PdfSignatureConfig pdfSignatureConfig;

    /**
     * 获取pdf大纲
     *
     * @param downloadPath
     * @return
     */
    @Override
    public List<HashMap<String, Object>> getPdfOutline(String downloadPath) {
        return parsePDFOutlines(downloadPath);
    }

    /**
     * 通过关键字获取xy
     *
     * @param pdfReader
     * @param keyword
     * @return
     */
    @Override
    public List<KeywordInfo> getSealLocation(PdfReader pdfReader, String keyword) throws IOException {
        return getSealLocationKeyword(pdfReader, keyword);
    }

    /**
     * 完成盖章和签名
     *
     * @param stampList
     */
    @Override
    public void completeStamp(List<Stamp> stampList) {
        String localPath = RuoYiConfig.getProfile();

        for (Stamp stamp : stampList) {
            if (ObjectUtil.isNull(stamp.getX())) {
                throw new DataException("x轴为空");
            }

            if (ObjectUtil.isNull(stamp.getPage())) {
                throw new DataException("page为空");
            }

            if (ObjectUtil.isNull(stamp.getWidth())) {
                throw new DataException("width为空");
            }

            if (ObjectUtil.isNull(stamp.getHeight())) {
                throw new DataException("height为空");
            }

            // 获取pdf文件路径
            String pathPdf = StringUtils.substringAfter(stamp.getFilePath(), Constants.RESOURCE_PREFIX);
            String inputPdfPathPdf = localPath + pathPdf;
            String outPdfPathPdf = localPath + "/temp" + pathPdf;
            // 获取章图片路径
            String pathSura = StringUtils.substringAfter(stamp.getImagesUrl(), Constants.RESOURCE_PREFIX);
            String inputPdfPathSura = localPath + pathSura;


            PdfSignatureConfig pdfConfig =
                    new PdfSignatureConfig(
                            ResourceUtil.getStream(pdfSignatureConfig.getPath()),
                            pdfSignatureConfig.getPassword(),
                            pdfSignatureConfig.getName(),
                            pdfSignatureConfig.getPhone(),
                            pdfSignatureConfig.getPlace(),
                            pdfSignatureConfig.getCause());

            PdfUtils.stamp(pdfConfig, inputPdfPathPdf, outPdfPathPdf, inputPdfPathSura, stamp.getX(), stamp.getY(), stamp.getWidth(), stamp.getHeight(), stamp.getPage());
        }
    }

    /**
     * 获取签章位置（关键字位置）
     *
     * @param pdfReader
     * @param signKeyWord
     * @return List<KeywordInfo>
     * @throws GeneralSecurityException
     * @throws IOException
     * @throws DocumentException
     */
    private List<KeywordInfo> getSealLocationKeyword(PdfReader pdfReader, String signKeyWord) throws IOException {
        // 读取pdf内容
        int pageNum = pdfReader.getNumberOfPages();
        // 签章位置对象
        List<KeywordInfo> keywordList = new LinkedList<>();

        // 下标从1开始
        for (int page = 1; page <= pageNum; page++) {
            KeywordInfo rectangleCentreBase = new KeywordInfo();
            rectangleCentreBase.setPage(page);
            PdfReaderContentParser pdfReaderContentParser = new PdfReaderContentParser(pdfReader);
            pdfReaderContentParser.processContent(page, new RenderListener() {

                StringBuilder sb = new StringBuilder("");
                int maxLength = signKeyWord.length();

                @Override
                public void renderText(TextRenderInfo textRenderInfo) {
                    // 只适用 单字块文档 以及 关键字整个为一个块的情况
                    // 设置 关键字文本为单独的块，不然会错位
                    boolean isKeywordChunk = textRenderInfo.getText().length() == maxLength;
                    if (isKeywordChunk) {
                        // 文档按照 块 读取
                        sb.delete(0, sb.length());
                        sb.append(textRenderInfo.getText());
                    } else if (textRenderInfo.getText().length() < maxLength) {
                        // 有些文档 单字一个块的情况
                        // 拼接字符串
                        sb.append(textRenderInfo.getText());
                        // 去除首部字符串，使长度等于关键字长度
                        if (sb.length() > maxLength) {
                            sb.delete(0, sb.length() - maxLength);
                        }
                    } else if (textRenderInfo.getText().contains(signKeyWord)) {
                        sb.delete(0, sb.length());
                        sb.append(signKeyWord);
                    }
                    // 判断是否匹配上
                    if (signKeyWord.equals(sb.toString())) {
                        KeywordInfo keywordInfo = rectangleCentreBase.copy();

                        // 计算中心点坐标

                        com.itextpdf.awt.geom.Rectangle2D.Float baseFloat = textRenderInfo.getBaseline()
                                .getBoundingRectange();
                        com.itextpdf.awt.geom.Rectangle2D.Float ascentFloat = textRenderInfo.getAscentLine()
                                .getBoundingRectange();

                        float centreX;
                        float centreY;
                        if (isKeywordChunk) {
                            centreX = baseFloat.x + ascentFloat.width / 2;
                            centreY = baseFloat.y + ((ascentFloat.y - baseFloat.y) / 2);
                        } else if (textRenderInfo.getText().length() < maxLength) {
                            centreX = baseFloat.x + ascentFloat.width - (maxLength * ascentFloat.width / 2);
                            centreY = baseFloat.y + ((ascentFloat.y - baseFloat.y) / 2);
                        } else {
                            int length = textRenderInfo.getText().length();
                            int index = textRenderInfo.getText().indexOf(signKeyWord);
                            centreX = baseFloat.x + ascentFloat.width / length * (index + 1);
                            centreY = baseFloat.y + ((ascentFloat.y - baseFloat.y) / 2);
                        }

                        keywordInfo.setCentreX(centreX);
                        keywordInfo.setCentreY(centreY);
                        keywordList.add(keywordInfo);
                        // 匹配完后 清除
                        sb.delete(0, sb.length());
                    }
                }

                @Override
                public void renderImage(ImageRenderInfo arg0) {
                }

                @Override
                public void endTextBlock() {
                }

                @Override
                public void beginTextBlock() {
                }
            });
        }
        return keywordList;
    }

    private List<HashMap<String, Object>> parsePDFOutlines(String filePath) {
        List<HashMap<String, Object>> outlines = new ArrayList<>();
        try {
            PdfReader reader = new PdfReader(filePath);
            List<HashMap<String, Object>> bookmarks = SimpleBookmark.getBookmark(reader);
            if (bookmarks != null) {
                processOutlines(bookmarks, outlines);
            }
            reader.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return outlines;
    }

    private void processOutlines(List<HashMap<String, Object>> bookmarks, List<HashMap<String, Object>> outlines) {
        for (HashMap<String, Object> bookmark : bookmarks) {
            HashMap<String, Object> outline = new HashMap<>(16);
            outline.put("title", bookmark.get("Title"));
            if (ObjectUtil.isNotEmpty(bookmark.get("Page"))) {
                outline.put("page", bookmark.get("Page").toString().split(" ")[0]);
            }
            if (bookmark.containsKey("Kids")) {
                List<HashMap<String, Object>> kids = (List<HashMap<String, Object>>) bookmark.get("Kids");
                List<HashMap<String, Object>> children = new ArrayList<>();
                outline.put("children", children);
                processOutlines(kids, children);
            }
            outlines.add(outline);
        }
    }
}
